package game.Player;


import game.Gamemode.GamemodeManager;
import game.Gamemode.StandardManager;
import game.Gamemode.TeamDeathmatchManager;
import game.Helper;
import game.Map.Map;
import game.Rendering.Camera;
import game.Scene;
import game.Vector3;
import game.Weapons.Weapon_info;

import java.awt.*;
import java.awt.geom.Line2D;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;

//Controller for the AI
public class BotController extends Controller {

    public enum Difficulty {
        Harmless,
        Easy,
        Medium,
        Hard,
        Impossible
    }

    public static Difficulty difficultyFromString(String s) {
        Difficulty res = Difficulty.Easy;
        s = s.toLowerCase();
        if (s.contains("low") || s.contains("easy")) {
            res = Difficulty.Easy;
        } else if (s.contains("normal") || s.contains("medium")) {
            res = Difficulty.Medium;
        } else if (s.contains("difficult") || s.contains("hard") || s.contains("advanced")) {
            res = Difficulty.Hard;
        }
        return res;
    }

    //Bot Config values
    protected final double targetSetReactionTime; // Reaction time from target seen to target set
    protected final double shootReactionTime; // Reaction time from target seen to target set
    protected final double imprecision; // Higher -> worse accuracy
    protected final double lerp; // The lower -> slower rotation

    protected Point tilePos, targetTilePos;
    private int behavior; //-2 stands still until order is given, -1 doesnt update at all, 0 chases player, 1 wanders
    private boolean busy = false;
    private boolean needsHelp = true;
    private double rotationSpeed = 100;
    private boolean aimingAtEnemy = false;

    public List<Vector3> a_star_path;
    protected Vector3 a_star_goal;

    protected Difficulty botLevel;
    Player targetPlayer;
    protected boolean shootingTarget;
    protected long timeToContinueMoving;
    protected long timeToSetTarget;
    protected long timeToStartShooting;
    protected long lastTargetSeenTime;


    String teamMessageToSend;
    boolean sendingTeamMessage;
    long timeToSendTeamMessage;

    // Used to control changes
    boolean wasFrozen;
    boolean wasAlive;

    List<Integer> visited;
    boolean notified = false;


    /**
     * Constructor.
     *
     * @param player   Player controlled by the controller.
     * @param behavior Behavior the player will follow.
     */
    public BotController(Player player, int behavior, Difficulty botLevel) throws Exception {
        super(player);
        this.behavior = behavior;
        this.tilePos = new Point(0, 0);
        this.targetTilePos = new Point(0, 0);
        this.a_star_path = new ArrayList<Vector3>();
        this.visited = new ArrayList<>();
        this.botLevel = botLevel;
        if (botLevel == Difficulty.Easy) {
            targetSetReactionTime = 1.5;
            shootReactionTime = 1.5;
            imprecision = 2.0;
            lerp = 0.025;
        } else if (botLevel == Difficulty.Medium) {
            targetSetReactionTime = 1.0;
            shootReactionTime = 1.0;
            imprecision = 1.0;
            lerp = 0.05;
        } else if (botLevel == Difficulty.Hard) {
            targetSetReactionTime = 0.25;
            shootReactionTime = 0.25;
            imprecision = 0.5;
            lerp = 0.1;
        } else {
            // Harmless
            targetSetReactionTime = 0.5;
            shootReactionTime = 500.0;
            imprecision = 0.5;
            lerp = 0.05;
        }
        targetPlayer = null;
        wasFrozen = true;

    }

    protected int random(int min, int max) {
        int rnd = (int) (Math.random() * (max - min)) + min; // max not included
        return rnd;
    }

    public void aim(Vector3 position, double deltaTime) {
        Vector3 myPos = this.player.getPosition().add(0);
        Vector3 dir1 = position.sub(myPos); //Vector I want to be looking at

        double rotationObj = Math.atan2(dir1.y, dir1.x);
        double rotation = this.player.getRotation();

        double dir = (rotationObj - rotation + 3 * Math.PI) % (2 * Math.PI) - Math.PI;
        dir = rotation + dir * rotationSpeed * deltaTime;
        dir = ((dir % (2 * Math.PI)) + (2 * Math.PI)) % (2 * Math.PI);
        player.setRotation(dir);
    }

    protected double normalizeAngle(double angle) {
        return Math.atan2(Math.sin(angle), Math.cos(angle));
    }

    /**
     * Called on every frame, updates player position and rotation.
     *
     * @param deltaTime Time passed since last frame was rendered.
     */
    //TODO: Rotation
    public void update(double deltaTime) {
        update_vals();
        bot_update(deltaTime);
    }

    @Override
    public void lateupdate(double deltaTime) {
        if (player.isAlive() && Scene.getInstance() != null && Scene.getInstance().getCamera() != null) {
            if (a_star_path != null && a_star_path.size() >= 1) {
                Camera cam = Scene.getInstance().getCamera();
                Vector3 currPos = this.player.getPosition();

                for (int i = 0; i < a_star_path.size(); i++) {
                    Vector3 nextPos = a_star_path.get(i);
                    double tileSize = Scene.getInstance().getMap().getTileSize();
                    nextPos = nextPos.add(new Vector3(tileSize / 2,
                            tileSize / 2, 0.0));

                    cam.addDebugLine(new Line2D.Double(currPos.x, currPos.y, nextPos.x, nextPos.y));
                    currPos = nextPos;
                }
            }
        }
    }

    protected void bot_update(double deltaTime) {
        if (this.player.isAlive() && this.player.freeze) {
            // Avoid lag when all bots check for path at the same time
            if (this.a_star_path == null && random(0, 100) < 5) {
                get_random_path();
            }
        }
        if (this.player.isAlive() && !this.player.freeze) {
            if (!this.player.isPlanting() && !this.player.isDefusing()) {
                Point newTilePos = Scene.getInstance().getMap().globalToTilePos(this.player.getPosition());
                if (!newTilePos.equals(this.tilePos)) {
                    this.tilePos = newTilePos;
                    onTileChange(deltaTime);
                }

                // Call to onTileChange()
                if (targetPlayer != null) {
                    newTilePos = Scene.getInstance().getMap().globalToTilePos(this.targetPlayer.getPosition());
                    if (!newTilePos.equals(this.targetTilePos)) {
                        this.targetTilePos = newTilePos;
                        onTargetTileChange(deltaTime);
                    }
                    lastTargetSeenTime = System.currentTimeMillis();
                }
            }

            // Change to best gun available
            change_to_best_gun();

            // Check enemies in vision
            List<Player> enemies = this.player.viewableEnemies();
            Player closestEnemy = this.player.closestViewableEnemy();
            if (enemies.isEmpty()) {
                // Forget target player if any (planned path is still there)
                targetPlayer = null;
                this.shootingTarget = false;
                timeToSetTarget = System.currentTimeMillis() + (int) (targetSetReactionTime * 1000);

                // Check for new random goal
                if (a_star_path == null || a_star_path.size() < 1) {
                    get_random_path();
                }

                // Check new enemy target
            } else if (targetPlayer == null ||
                    !enemies.contains(targetPlayer) ||
                    (timeToSetTarget < System.currentTimeMillis() && closestEnemy != targetPlayer)) {

                // Send message to chat box
                if (targetPlayer == null) {
                    botSawEnemy();
                }

                //Set new target player from viewable enemies (closest enemy)
                this.targetPlayer = closestEnemy;
                this.timeToSetTarget = System.currentTimeMillis() + random(1000, 4000);
                this.shootingTarget = false;
                this.timeToContinueMoving = System.currentTimeMillis() - 1000;

                //System.out.println("BOT saw enemy: " + targetPlayer.getName());

                this.a_star_goal = this.targetPlayer.getPosition();
                this.a_star_path = Helper.mainScene.getMap().a_star(this.a_star_goal, this.player.getPosition());
                this.targetTilePos = Scene.getInstance().getMap().globalToTilePos(this.targetPlayer.getPosition());
            } else {
                //Go to targetPlayer (it is already set and was recently set)

            }

            //Plant/ Defuse the bomb
            long remainingTime = this.player.getShortestRemainingTime() / 100;
            GamemodeManager gm = GamemodeManager.getInstance();
            if (gm instanceof StandardManager) {
                //CT behavior (attempt to defuse even if its last player)
                if (this.player.getTeam() == ((StandardManager) gm).getCounterTeam()) {
                    if (enemies.isEmpty() || (this.botLevel == Difficulty.Hard && remainingTime <= this.player.getHealth())) {
                        if (this.canSeeBomb()) {
                            if (this.player.isInDefusingRange()) {
                                this.player.tryToDefuseBomb();
                            } else {
                                //If can see bomb but not in range to defuse get close to it
                                this.player.stopDefusing();
                                if (this.targetPlayer == null && this.a_star_goal != ((StandardManager) gm).getBombPosition()) {
                                    this.a_star_goal = ((StandardManager) gm).getBombPosition();
                                    this.a_star_path = Helper.mainScene.getMap().a_star(this.a_star_goal, this.player.getPosition());
                                }
                            }
                        } else this.player.stopDefusing();
                    } else this.player.stopDefusing();
                }
                //T behavior (when there is an enemy nearby attempt to plant only if bot isnt the last player)
                else {
                    if (enemies.isEmpty() || (this.botLevel == Difficulty.Hard && remainingTime <= this.player.getHealth() && ((StandardManager) gm).aliveTerroristsCount() > 1)) {
                        if (this.player.hasBomb() && this.player.isInBombZone()) {
                            this.player.tryToPlantBomb();
                        } else this.player.stopPlanting();
                    } else this.player.stopPlanting();
                }
            }


            // ROTATION
            double aimAngle = 0.0;
            Vector3 thisPos = player.getPosition();
            thisPos.z = 0.0;


            if (targetPlayer != null) {
                // AIM AT TARGET PLAYER
                Vector3 targetPos = targetPlayer.getPosition();
                targetPos.z = 0.0;

                Vector3 dir = targetPos.add(
                        new Vector3((Math.random() - 0.5) * 2 * imprecision, (Math.random() - 0.5) * 2 * imprecision, 0.0)
                ).sub(thisPos).normalized();
                aimAngle = Math.atan2(dir.y, dir.x);

            } else if (!this.a_star_path.isEmpty()) {
                // AIM AT NEXT NODE IN PATH
                double tileSize = Scene.getInstance().getMap().getTileSize();
                Vector3 nodePos = this.a_star_path.get(0);
                int nodeIndexToLook = this.a_star_path.size() - 1;
                if (nodeIndexToLook > 8)
                    nodeIndexToLook = 8;
                nodePos = nodePos.add(a_star_path.get(nodeIndexToLook)).divide(2.0);

                Vector3 nodeCenter = nodePos.add(new Vector3(tileSize / 2,
                        tileSize / 2, 0.0));

                Vector3 dir = nodeCenter.sub(thisPos).normalized();
                aimAngle = Math.atan2(dir.y, dir.x);
            }

            double lerpedAngle = this.player.getRotation() + normalizeAngle(aimAngle - this.player.getRotation());
            lerpedAngle = normalizeAngle(this.player.getRotation() * (1.0 - lerp) + lerpedAngle * lerp);
            this.player.setRotation(lerpedAngle);

            // FOLLOW PATH
            if ((!this.a_star_path.isEmpty() && timeToContinueMoving <= System.currentTimeMillis() ||
                    this.targetPlayer != null) && !this.player.isPlanting() && !this.player.isDefusing()) {
                Point playerTilePos = Scene.getInstance().getMap().globalToTilePos(thisPos);
                Vector3 nextNodePos = this.a_star_path.get(0);
                Point nodeTilePos = Scene.getInstance().getMap().globalToTilePos(nextNodePos);

                // Player reached next node
                if (playerTilePos.equals(nodeTilePos)) {
                    double tileSize = Scene.getInstance().getMap().getTileSize();
                    Vector3 nodeCenter = nextNodePos.add(new Vector3(tileSize / 2, tileSize / 2, 0.0));


                    if (this.a_star_path.size() > 1) {
                        Vector3 nextNodeCenter = this.a_star_path.get(1).add(new Vector3(tileSize / 2, tileSize / 2, 0.0));
                        if ((Vector3.distance(nodeCenter, thisPos) < tileSize / 8
                                || Vector3.distance(nextNodeCenter, thisPos) < Scene.getInstance().getMap().getTileSize() / 2)) {
                            this.a_star_path.remove(0);
                            nextNodePos = this.a_star_path.get(0);
                        }
                    }else{
                        if (Vector3.distance(nodeCenter, thisPos) < tileSize / 8) {
                            this.a_star_path.remove(0);
                            this.a_star_path = null;
                        }
                    }
                }

                // Go to next node center
                double tileSize = Scene.getInstance().getMap().getTileSize();
                Vector3 nodeCenter = nextNodePos.add(new Vector3(tileSize / 2, tileSize / 2, 0.0));
                Vector3 dir = nodeCenter.sub(thisPos).normalized();
                this.player.playerSpeed = dir.multiply(this.player.runSpeed);
            } else {
                this.player.playerSpeed = new Vector3(0.0, 0.0, 0.0);
            }


            // There is a target enemy and it is visible
            if (targetPlayer != null && enemies.contains(targetPlayer)) {

                // NO wall between
                if (Scene.getInstance().getMap().wall_between(this.player.getPosition(),
                        targetPlayer.getPosition()) == null) {

                    //Shoot
                    if (!shootingTarget) {
                        shootingTarget = true;
                        timeToStartShooting = System.currentTimeMillis() + (int) (shootReactionTime * 1000);
                        timeToContinueMoving = System.currentTimeMillis() + random(200, 3000);
                    }


                    double targetDist = Vector3.distance(new Vector3(player.getX(), player.getY(), 0),
                            new Vector3(targetPlayer.getX(), targetPlayer.getY(), 0));

                    // Wait for reaction time to start shooting
                    if (timeToStartShooting < System.currentTimeMillis()) {
                        try {
                            if (player.current_weapon != player.knife || targetDist < 1.2) {
                                if (!this.player.isDefusing() && !this.player.isPlanting()) {
                                    this.player.shoot();
                                    this.player.stopDefusing();
                                    this.player.stopPlanting();
                                }
                            }
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }


                    if (targetDist < 6.0) {
                        // Stop moving
                        if (!shootingTarget || timeToContinueMoving > System.currentTimeMillis() ||
                                (targetDist < 1.7 && player.current_weapon != player.knife) || targetDist < 0.7)
                            this.player.playerSpeed = new Vector3(0.0, 0.0, 0.0);
                    }


                }

            }
        }
    }

    /**
     * Updates the path with the closest non visited bomb spot
     */
    private void get_closest_bomb_path() {
        Map m = Helper.mainScene.getMap();
        List<Map.BombPoint> bombPoints = m.getBombPoints();
        if (visited.size() != bombPoints.size()) {
            int closestIdx = -1;
            int minLen = Integer.MAX_VALUE;
            List<Vector3> shortestPath = new ArrayList<>();

            for (int i = 0; i < bombPoints.size(); i++) {
                if (!visited.contains(i)) {
                    List<Vector3> path = m.a_star(bombPoints.get(i).getCenter(), this.player.getPosition());
                    if (path.size() < minLen) {
                        minLen = path.size();
                        closestIdx = i;
                        shortestPath = path;
                    }
                }
            }
            visited.add(closestIdx);
            this.a_star_path = shortestPath;
            this.a_star_goal = bombPoints.get(closestIdx).getCenter();
        } else {
            Random rand = new Random(System.nanoTime());
            this.a_star_goal = Helper.mainScene.getMap().getBombPoints().get(rand.nextInt(Helper.mainScene.getMap().getBombPoints().size())).getCenter();
            this.a_star_path = Helper.mainScene.getMap().a_star(this.a_star_goal, this.player.getPosition());
        }

    }

    protected void get_random_path() {
        //System.out.println("BOT [" + player.getName() + "]: Getting new random walk...");
        // Search new goal tile
        Random rand = new Random(System.nanoTime());
        GamemodeManager gm = GamemodeManager.getInstance();
        Map map = Helper.mainScene.getMap();
        while (this.a_star_path == null || this.a_star_path.size() <= 1) {
            //If bomb is planted and there is few time left
            if (gm instanceof StandardManager && ((StandardManager) gm).isBombPlanted() && gm.getRemainingTime() < 6) {
                this.getAway(((StandardManager) gm).getBombPosition(), 10); //TODO: Use real radius?
            }
            //If bomb is planted Counter Terrorists try to check every bomb point to find and defuse the bomb
            else if (gm instanceof StandardManager && ((StandardManager) gm).isBombPlanted() && this.player.getTeam() == ((StandardManager) gm).getCounterTeam()) {
                get_closest_bomb_path();
            }
            //If bomb is planted Terrorists have a high chance of looping around the bomb
            else if (gm instanceof StandardManager && ((StandardManager) gm).isBombPlanted() && this.player.getTeam() == ((StandardManager) gm).getTerroristTeam()) {
                if (rand.nextDouble() > 0.95) {
                    this.a_star_goal = map.getRandomWalkablePosition(rand);
                    this.a_star_path = map.a_star(this.a_star_goal, this.player.getPosition());
                } else {
                    Vector3 bombPos = ((StandardManager) gm).getBombPosition();
                    //Get a random position around the bomb
                    this.a_star_goal = new Vector3(bombPos.x + (rand.nextInt(10) - 5) * map.getTileSize(), (bombPos.y + rand.nextInt(10) - 5) * map.getTileSize(), 0);
                    this.a_star_path = map.a_star(this.a_star_goal, this.player.getPosition());
                }
            }
            //If bomb is dropped everybody goes to it to either defend or pick it up
            else if (gm instanceof StandardManager && ((StandardManager) gm).isBombDropped()) {
                Vector3 droppedBombPos = ((StandardManager) gm).getDroppedBombPosition();
                //CTs loop around it
                if (this.player.getTeam() == ((StandardManager) gm).getCounterTeam()) {
                    this.a_star_goal = new Vector3(droppedBombPos.x + (rand.nextInt(10) - 5) * map.getTileSize(), (droppedBombPos.y + rand.nextInt(10) - 5) * map.getTileSize(), 0);
                    this.a_star_path = map.a_star(this.a_star_goal, this.player.getPosition());

                }
                //Ts go straight to the bomb
                else {
                    this.a_star_goal = new Vector3(droppedBombPos.x, droppedBombPos.y, 0);
                    this.a_star_path = map.a_star(this.a_star_goal, this.player.getPosition());
                }
            } else if (gm instanceof TeamDeathmatchManager || (gm instanceof StandardManager && rand.nextDouble() > 0.9)) {
                this.a_star_goal = map.getRandomWalkablePosition(rand);
                this.a_star_path = map.a_star(this.a_star_goal, this.player.getPosition());
            } else {
                this.a_star_goal = map.getBombPoints().get(rand.nextInt(Helper.mainScene.getMap().getBombPoints().size())).getCenter();
                this.a_star_path = map.a_star(this.a_star_goal, this.player.getPosition());
            }
        }
        //TODO paremetrizar
        this.timeToContinueMoving = System.currentTimeMillis() + random(0, 1000);
    }

    protected void change_to_best_gun() {
        // Check to change gun
        if ((player.primary_weapon != null && player.current_weapon != player.primary_weapon && player.primary_weapon.getAmmo_total() > 0)
                && (targetPlayer == null || (targetPlayer != null && player.primary_weapon.getAmmo_charger() > 0))) {
            player.changeToPrimary();
        } else if (((player.primary_weapon == null || player.primary_weapon.getAmmo_total() <= 0) &&
                player.secondary_weapon != null && player.current_weapon != player.secondary_weapon && player.secondary_weapon.getAmmo_total() > 0)
                || (this.botLevel == Difficulty.Hard && this.player.getHealth() <= 50 && this.player.current_weapon == this.player.primary_weapon && this.player.current_weapon.getAmmo_charger() == 0)) {
            player.changeToSecondary();
        } else if ((player.primary_weapon == null || player.primary_weapon.getAmmo_total() <= 0) &&
                (player.secondary_weapon == null || player.secondary_weapon.getAmmo_total() <= 0)) {
            player.changeToKnife();
        }

        if (botLevel == Difficulty.Hard && targetPlayer == null && player.current_weapon != player.knife) {
            if (System.currentTimeMillis() > lastTargetSeenTime + 1000)
                player.changeToKnife();
        }
    }

    protected void update_vals() {
        // Send message that is waiting to be sent
        if (this.sendingTeamMessage && this.timeToSendTeamMessage < System.currentTimeMillis()) {
            this.sendingTeamMessage = false;
            this.player.sendTeamMessage(teamMessageToSend);
        }

        // Notify kill
        if (!this.player.isAlive() && wasAlive) {
            // Bot just got killed
            botGotKilled();
        }
        wasAlive = this.player.isAlive();

        if (this.player.freeze) {

        }
        wasFrozen = this.player.freeze;
    }

    /**
     * Updates the path of the player depending on its behavior.
     */
    public void updateStarPath() {
        //this.a_star_goal = this.targetPlayer.getPosition();
        this.a_star_path = Helper.mainScene.getMap().a_star(this.a_star_goal, this.player.getPosition());
    }

    /**
     * Called whenever the target player changes tile.
     */
    public void onTargetTileChange(double deltaTime) {
        this.a_star_goal = targetPlayer.getPosition();
        this.updateStarPath();
    }

    /**
     * Called whenever the player changes tile.
     */
    public void onTileChange(double deltaTime) {
        if (this.a_star_path == null || this.a_star_path.size() == 0) {
            return;
        }
        // Check if new position in path, else recalculate
        boolean found = false;
        for (Vector3 path_pos : this.a_star_path) {
            Point path_tile = Scene.getInstance().getMap().globalToTilePos(path_pos);
            if (path_tile.equals(this.tilePos)) {
                found = true;
            }
        }

        if (!found) {
            this.updateStarPath();
        }
        notified = false;
    }

    public boolean isBusy() {
        return busy;
    }

    public void sendHelp(Player p) {

        //System.out.println("HELP SENT");
        this.a_star_goal = p.getPosition();
        this.a_star_path = Helper.mainScene.getMap().a_star(this.player.getPosition(), this.a_star_goal);
        this.busy = true;
    }


    public void goTo(Vector3 p) {
        this.a_star_goal = p;
        this.a_star_path = Helper.mainScene.getMap().a_star(this.player.getPosition(), this.a_star_goal);
    }

    public void notifyRoundStart() {
        // Reset targets
        this.a_star_goal = null;
        this.a_star_path = null;
        this.targetPlayer = null;
        Random rand = new Random(System.nanoTime()); //instance of random class
        List<Weapon_info> weapons = this.player.getTeam().getWeapons();
        List<Weapon_info> buyablePrimaryWeapons = new ArrayList<>();
        List<Weapon_info> buyableSecondaryWeapons = new ArrayList<>();
        List<Weapon_info> buyableArmor = new ArrayList<>();
        Weapon_info defuse_kit = null;
        for (Weapon_info w : weapons) {
            if (!w.getName().contains("knife") && w.isPrimary() &&
                    w.getPrice() <= this.player.getMoney()) buyablePrimaryWeapons.add(w);
            else if (w.getType() == 0 && w.getPrice() <= this.player.getMoney()) buyableSecondaryWeapons.add(w);
            else if (w.getType() == 6) {
                if (w.getName().equals("defusekit")) defuse_kit = w;
                else buyableArmor.add(w);
            }
        }

        //Try to buy a primary weapon if bot doesn't have one yet
        //System.out.println(player.getName() + " started round with " + player.getMoney() + " money");
        boolean bought = false;
        if (this.player.getPrimary_weapon() == null) {
            int index;
            if (buyablePrimaryWeapons.size() > 0) {
                index = rand.nextInt(buyablePrimaryWeapons.size());
                bought = this.player.tryToBuy(buyablePrimaryWeapons.get(index));
            }
            if (!bought) {
                //If didn't buy primary, buy secondary with 50% chance
                if (buyableSecondaryWeapons.size() > 0 && rand.nextDouble() > 0.5) {
                    index = rand.nextInt(buyableSecondaryWeapons.size());
                    this.player.tryToBuy(buyableSecondaryWeapons.get(index));
                }
            }
        }
        //Buy defuse kit with 75% chance
        if (GamemodeManager.getInstance() instanceof StandardManager) {
            if (this.player.team == ((StandardManager) GamemodeManager.getInstance()).getCounterTeam() && rand.nextDouble() < 0.75) {
                this.player.tryToBuy(defuse_kit);
            }
        }
        //Buy armor with 75% chance
        if (rand.nextDouble() < 0.75) {
            bought = this.player.tryToBuy(buyableArmor.get(0));
            //Upgrade armor with 75% chance
            if (bought && rand.nextDouble() < 0.75) this.player.tryToBuy(buyableArmor.get(1));
        }


        if (this.player.primary_weapon != null) {
            this.player.current_weapon = this.player.primary_weapon;
        } else {
            this.player.current_weapon = this.player.secondary_weapon;
        }
    }

    /**
     * Send a message in the chat box after a determined time.
     *
     * @param msg        Message to send.
     * @param timeToWait Time to wait to send the message in seconds.
     */
    protected void sendTeamMessage(String msg, double timeToWait) {
        if (this.sendingTeamMessage) {
            return;
        }

        this.sendingTeamMessage = true;
        this.teamMessageToSend = msg;
        this.timeToSendTeamMessage = System.currentTimeMillis() + (long) (timeToWait * 1000);
    }


    // OTHER EXTRA STUFF
    protected void botGotKilled() {
        if (random(0, 100) < 10)
            sendTeamMessage(getEnemyKilledMessage(), 1);
    }

    protected void teamDamaged() {
        if (random(0, 100) < 10)
            sendTeamMessage(getTeamKilledMessage(), 1);
    }

    protected void botSawEnemy() {
        if (random(0, 100) < 10)
            sendTeamMessage(getEnemySpottedMessage(), 1);
    }

    // BOT RESOURCES (NAMES, CHAT MESSAGES)
    static int botNameIndex = 0;
    static String[] botNames = {
            "Adam", "RamPage_777", "xXx_CamPer_xXx", "Firendly_Killer", "Noobi3", "Player007", "pericoPalotes",
            "pelotary13", "gusan0_a$$esino", "RubiusOMG", "estupid.0"};

    public static String getBotName() {
        botNameIndex = (botNameIndex + 1) % botNames.length;
        return botNames[botNameIndex];
    }

    static int enemyKilledIndex = 0;
    static String[] enemyKilledMsgs = {"unbelievable :/", "omg", "what!?", "come on man", "wtf",
            "im getting tired of this", "hax man, its so obvious", "turn those hax offff", "so annoying"};

    public static String getEnemyKilledMessage() {
        enemyKilledIndex = (enemyKilledIndex + 1) % enemyKilledMsgs.length;
        return enemyKilledMsgs[enemyKilledIndex];
    }


    static int teamKilledIndex = 0;
    static String[] teamKilledMsgs = {"what are you doing man?",
            "im getting pissed at this dude", "im gonna start killing you if you dont stop",
            "STOPP IT RIGHT NOW OR IM CALLING YOUR MOM MAN!", "ILL KILL YOUR WHOLE FAMILY"};

    public static String getTeamKilledMessage() {
        teamKilledIndex = (teamKilledIndex + 1) % teamKilledMsgs.length;
        return teamKilledMsgs[teamKilledIndex];
    }


    static int enemySpottedIndex = 0;
    static String[] enemySpottedMsgs = {"Enemy spotted.", "I see them!", "they're here", "Let´s have some fun!",
            "need backup", "come here guys"};

    public static String getEnemySpottedMessage() {
        enemySpottedIndex = (enemySpottedIndex + 1) % enemySpottedMsgs.length;
        return enemySpottedMsgs[enemySpottedIndex];
    }

    //TODO
    public void notifyDamageDone(Player inflictor) {
        if (targetPlayer == null && inflictor.getTeam() != player.getTeam()) {
            this.a_star_goal = inflictor.getPosition();
            this.a_star_path = Helper.mainScene.getMap().a_star(this.a_star_goal, this.player.getPosition());
            this.targetTilePos = Scene.getInstance().getMap().globalToTilePos(inflictor.getPosition());
        } else if (inflictor.getTeam() == player.getTeam()) {
            teamDamaged();
        }
    }

    public void notifyShotFired(Player shooter) {
        if (targetPlayer == null && this.botLevel != Difficulty.Easy && !notified) {
            notified = true;
            this.a_star_goal = shooter.getPosition();
            this.a_star_path = Helper.mainScene.getMap().a_star(this.a_star_goal, this.player.getPosition());
            this.targetTilePos = Scene.getInstance().getMap().globalToTilePos(shooter.getPosition());
            if (this.a_star_path.size() > 3 && this.botLevel == Difficulty.Medium) {
                this.a_star_goal = this.a_star_path.get(2);
                this.a_star_path = this.a_star_path.subList(0, 3);
                this.targetTilePos = Scene.getInstance().getMap().globalToTilePos(this.a_star_goal);
            } else if (this.a_star_path.size() > 6 && this.botLevel == Difficulty.Hard) {
                this.a_star_goal = this.a_star_path.get(5);
                this.a_star_path = this.a_star_path.subList(0, 6);
                this.targetTilePos = Scene.getInstance().getMap().globalToTilePos(this.a_star_goal);
            }
        }
    }

    private boolean canSeeBomb() {
        GamemodeManager gm = GamemodeManager.getInstance();
        if (gm instanceof StandardManager && ((StandardManager) gm).getBombPosition() != null) {
            Vector3 bombPos = ((StandardManager) GamemodeManager.getInstance()).getBombPosition();
            Vector3 dif = new Vector3(this.player.getPosition().x - bombPos.x, this.player.getPosition().y - bombPos.y, 0);
            return dif.mod() <= 3 * Helper.mainScene.getMap().getTileSize();
        }
        return false;
    }

    public void notifyBombDrop() {
        GamemodeManager gm = GamemodeManager.getInstance();
        Map map = Helper.mainScene.getMap();
        //If bot has no target go to dropped bomb position
        if (this.targetPlayer == null) {
            Vector3 droppedBombPos = ((StandardManager) gm).getDroppedBombPosition();
            this.a_star_goal = new Vector3(droppedBombPos.x, droppedBombPos.y, 0);
            this.a_star_path = map.a_star(this.a_star_goal, this.player.getPosition());
        }
    }

    //Method to make the bots run away from bomb explosion
    private void getAway(Vector3 pos, int radius) {
        Map map = Helper.mainScene.getMap();
        Random rand = new Random(System.nanoTime());
        Vector3 bombPos = new Vector3(pos.x, pos.y, 0); //To calculate distance
        while (this.a_star_path == null || this.a_star_path.size() <= 1) {
            Vector3 randomPos = new Vector3(pos.x + (rand.nextInt(2 * radius) - radius) * map.getTileSize(), (pos.y + rand.nextInt(2 * radius) - radius) * map.getTileSize(), 0);
            //If it is further than the current pos
            if (bombPos.sub(randomPos).mod() > this.player.getPosition().sub(randomPos).mod()) {
                this.a_star_goal = randomPos;
                this.a_star_path = map.a_star(this.a_star_goal, this.player.getPosition());
            }
        }
    }
}
